using System;
using System.Collections.Generic;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using UnityEditor;
using UnityEngine;

namespace Framework.Bundle
{
    public class BuildContext
    {
        internal IAssetBundleBuildProcessor Processor;
        internal BundleConfig Config;
        internal List<AssetItemData> Assets = new List<AssetItemData>();
        internal List<BundleAssetsData> Bundles = new List<BundleAssetsData>();
        internal AssetBundleManifest Manifest;
        internal void LogError(string message)
        {
            UnityEngine.Debug.LogError(message);
        }
        internal void LogWarning(string message)
        {
            UnityEngine.Debug.LogWarning(message);
        }
        internal void LogErrorFormat(string format, params object[] args)
        {
            UnityEngine.Debug.LogErrorFormat(format, args);

        }
    }
    internal class AssetItemData
    {
        public string AssetPath;
        public string BundleName;
        public bool IsEditor;
        public BundleItemData BundleInfo;
        public AssetItemData()
        {

        }
    }
    internal class BundleAssetsData
    {
        public string BundleName;
        public bool IsScene;
        public List<AssetItemData> Assets = new List<AssetItemData>();
        public BundleAssetsData()
        {

        }
    }

    internal class DependenceItemData
    {
        public string AssetPath;
        //被哪些bundle引用了
        public List<BundleAssetsData> Bundles = new List<BundleAssetsData>();
        public void ConnectBundle(BundleAssetsData value)
        {
            foreach (var item in Bundles)
            {
                if (item == value)
                {
                    return;
                }
            }
            Bundles.Add(value);
        }
    }
    internal class MetaData
    {
        //public string Version;
        public string BundleExtension;
        public string BuildTime;
        public string UnityVersion;
        public string BuildTarget;
        public int AssetCount;
        //public string UserData;
        public bool AppendHashToAssetBundleName;
        public MetaBundleInfo[] Bundles;

    }
    internal class MetaBundleInfo
    {
        public string Name;
        public string[] Assets;
        public int Hash0;
        public int Hash1;
        //public int Size;
        //public bool Encrypt;
        public string[] DirectDependencies;
        public MetaBundleInfo()
        {
        }
    }
    internal class BundleFileSetting
    {
        public string Path;
        public int Encrypt;
        public int BuiltIn;
        public BundleFileSetting()
        {

        }

    }
    public class BuildPipline
    {
        public BuildContext Context { get; private set; }
        private List<PiplineBase> list = new List<PiplineBase>();
        public BuildPipline()
        {
            Context = new BuildContext();

        }
        public bool Execute()
        {
            list.Clear();
            list.Add(new Setup());
            list.Add(new ReadConfig());
            list.Add(new CollectDependencies());
            list.Add(new BuildBundleInfo());
            list.Add(new Build());
            list.Add(new BuildManifest());
            list.Add(new Export());
            return doExe();
        }
        public void Export()
        {
            list.Clear();
            list.Add(new Setup());
            list.Add(new Export());
            doExe();
        }
        public void SetActiveTarget(BuildTarget value = BuildTarget.NoTarget)
        {
            list.Clear();
            list.Add(new Setup());
            list.Add(new SetBuildTarget(value));
            doExe();
        }
        public void Analyze()
        {
            list.Clear();
            list.Add(new Setup());
            list.Add(new ReadConfig());
            list.Add(new CollectDependencies());
            doExe();
        }
        private bool doExe()
        {
            var len = list.Count;
            for (var i = 0; i < len; i++)
            {
                var item = list[i];
                item.Context = this.Context;
                if (!item.Execute())
                {
                    Context.LogError("exe " + item.GetType().Name + " error");
                    return false;
                }
            }
            return true;
        }
    }
    internal class PiplineBase
    {
        internal BuildContext Context;
        public virtual bool Execute()
        {
            return false;
        }
        public virtual void Cleanup()
        {

        }
        protected string GetBundleName(string path)
        {
            return path.Replace("\\", "/").ToLower();
        }
        protected string GetBundleName(string path, bool hasHash = false)
        {
            var dir = Path.GetDirectoryName(path);
            var fileName = Path.GetFileNameWithoutExtension(path);
            path = Path.Combine(dir, fileName);
            if (hasHash)
            {
                var id = path.LastIndexOf("_");
                if (id > 0)
                {
                    path = path.Substring(0, id);
                }
            }
            return path.Replace("\\", "/").ToLower();
        }
    }
    internal class Setup : PiplineBase
    {
        public override bool Execute()
        {
            var config = BundleConfig.GetInstance();
            if (config == null)
            {
                return false;
            }
            if (config.BundleItems == null || config.BundleItems.Count < 1)
            {
                return false;
            }
            Context.Config = config;

            var cmds = new List<IAssetBundleBuildProcessor>();
            var typeList = GetTypeNames(typeof(IAssetBundleBuildProcessor), new string[] { "Assembly-CSharp-Editor" });
            foreach (var item in typeList)
            {
                if (System.Activator.CreateInstance(item) is IAssetBundleBuildProcessor cmd)
                {
                    cmds.Add(cmd);
                }
            }
            if (cmds.Count > 0)
            {
                Context.Processor = cmds[0];
                if (cmds.Count > 1)
                {
                    Debug.LogError("IAssetBundleBuildProcessor 只能有一个");
                }
            }
            return true;
        }
        private System.Type[] GetTypeNames(System.Type typeBase, string[] assemblyNames)
        {
            List<System.Type> typeNames = new List<Type>();
            foreach (string assemblyName in assemblyNames)
            {
                System.Reflection.Assembly assembly = System.Reflection.Assembly.Load(assemblyName);
                if (assembly == null)
                {
                    continue;
                }
                System.Type[] types = assembly.GetTypes();
                foreach (System.Type type in types)
                {
                    if (type.IsClass && !type.IsAbstract && typeBase.IsAssignableFrom(type))
                    {
                        typeNames.Add(type);
                    }
                }
            }
            return typeNames.ToArray();
        }

    }
    internal class SetBuildTarget : PiplineBase
    {
        private BuildTarget target;
        public SetBuildTarget(BuildTarget value)
        {
            if (value == BuildTarget.NoTarget)
            {
                value = UnityEditor.EditorUserBuildSettings.activeBuildTarget;
            }
            target = value;
        }
        public override bool Execute()
        {
            Context.Config.BuildTarget = target;
            return true;
        }
    }
    internal class ReadConfig : PiplineBase
    {
        private List<string> files;
        public override bool Execute()
        {
            var config = Context.Config;
            if (config.BuildTarget == BuildTarget.NoTarget)
            {
                return false;
            }
            Context.Assets.Clear();
            files = new List<string>();
            foreach (var item in config.BundleItems)
            {
                if (item.Source == null)
                {
                    continue;
                }
                string folderPath = AssetDatabase.GetAssetPath(item.Source);
                var list = GetAssetsInFolder(new string[] { folderPath }, item.AssetType);
                foreach (var file in list)
                {
                    var dir = Path.GetDirectoryName(file);
                    var BundleName = folderPath;
                    if (item.PackType == AssetPackType.Together)
                    {
                        BundleName = folderPath;
                    }
                    else if (item.PackType == AssetPackType.FileMode)
                    {
                        var fileName = Path.GetFileNameWithoutExtension(file);
                        BundleName = Path.Combine(dir, fileName);
                    }
                    else if (item.PackType == AssetPackType.FolderMode)
                    {
                        BundleName = dir;
                    }

                    else if (item.PackType == AssetPackType.SubFolderMode)
                    {
                        //第一个目录做为bundle名字
                        BundleName = folderPath;
                        var f = file.Replace(folderPath, "").Substring(1);
                        if (!string.IsNullOrEmpty(Path.GetDirectoryName(f)))
                        {
                            f = f.Replace("\\", "/");
                            var fid = f.IndexOf("/");
                            if (fid >= 0)
                            {
                                BundleName = Path.Combine(folderPath, f.Substring(0, fid));
                            }
                        }
                    }
                    var d = new AssetItemData();
                    d.BundleInfo = item;
                    d.AssetPath = file.Replace("\\", "/");
                    d.BundleName = BundleName.Replace("\\", "/");
                    Context.Assets.Add(d);
                }
            }
            return true;
        }
        private List<string> GetAssetsInFolder(string[] folderPath, AssetType assetType)
        {
            files.Clear();
            string filter = "t:" + assetType.ToString();
            var guids = AssetDatabase.FindAssets(filter, folderPath);
            foreach (var item in guids)
            {
                var path = AssetDatabase.GUIDToAssetPath(item);
                if (path.Contains("/~") || path.Contains("\\~"))
                {
                    continue;
                }
                // if (AssetDatabase.IsValidFolder(path)) continue;
                files.Add(path);

            }
            return files;
        }
    }
    internal class CollectDependencies : PiplineBase
    {
        public override bool Execute()
        {
            //资源路径:bundle
            var assetToBundleMap = new Dictionary<string, BundleAssetsData>();
            var bundleMap = new Dictionary<string, BundleAssetsData>();
            foreach (var item in Context.Assets)
            {
                if (!bundleMap.TryGetValue(item.BundleName, out var bundle))
                {
                    bundle = new BundleAssetsData
                    {
                        BundleName = item.BundleName
                    };
                    bundleMap.Add(item.BundleName, bundle);
                }
                bundle.Assets.Add(item);
                if (!assetToBundleMap.ContainsKey(item.AssetPath))
                {
                    assetToBundleMap.Add(item.AssetPath, bundle);
                }
            }
            var dependenceMap = new Dictionary<string, DependenceItemData>();
            foreach (var item in Context.Assets)
            {
                if (!bundleMap.TryGetValue(item.BundleName, out var bundle))
                {
                    continue;
                }

                string[] deps = AssetDatabase.GetDependencies(item.AssetPath, true);
                if (deps.Length < 1) continue;
                foreach (var path in deps)
                {
                    if (path == item.AssetPath)
                    {
                        continue;
                    }
                    var ext = Path.GetExtension(path).ToLower();
                    if (ext == ".cs") continue;
                    if (assetToBundleMap.ContainsKey(path))
                    {
                        // 如果资源已在配制路径则不处理
                        continue;
                    }
                    if (!dependenceMap.TryGetValue(path, out var dependenceItem))
                    {
                        dependenceItem = new DependenceItemData
                        {
                            AssetPath = path
                        };
                        dependenceMap.Add(path, dependenceItem);
                    }
                    dependenceItem.ConnectBundle(bundle);
                }
            }
            var builder = new StringBuilder();
            foreach (var item in dependenceMap)
            {
                if (item.Value.Bundles.Count < 2)
                {
                    //只被引用了一次不处理
                    continue;
                }
                AssetItemData addAsset;
                var path = item.Value.AssetPath.Replace("\\", "/");
                var isEditorRes = path.Contains("Editor/") || path.Contains("Gizmos/") || path.Contains("EditorResources/");
                if (isEditorRes)
                {
                    addAsset = new AssetItemData
                    {
                        AssetPath = item.Value.AssetPath.Replace("\\", "/"),
                        BundleName = "Editor",
                        IsEditor = true
                    };
                    Context.Assets.Add(addAsset);
                    continue;
                }

                var bundleName = Path.GetDirectoryName(path);

                if (path.StartsWith("Assets/"))
                {

                }
                else if (path.StartsWith("Packages/"))
                {
                    var dirs = path.Split("/");
                    if (dirs.Length > 2)
                    {
                        bundleName = dirs[0] + "/" + dirs[1];
                    }
                }
                builder.AppendLine(item.Value.AssetPath);
                foreach (var dep in item.Value.Bundles)
                {
                    builder.AppendLine("\t" + dep.BundleName);
                }
                addAsset = new AssetItemData
                {
                    AssetPath = item.Value.AssetPath.Replace("\\", "/"),
                    BundleName = bundleName.Replace("\\", "/"),
                };
                Context.Assets.Add(addAsset);
            }
            var targetPath = Context.Config.GetBuildCachePath();
            var AnalyzePath = Path.Combine(targetPath, "Analyze.json");
            try
            {
                if (!Directory.Exists(targetPath))
                {
                    Directory.CreateDirectory(targetPath);
                }
                if (File.Exists(AnalyzePath))
                {
                    File.Delete(AnalyzePath);
                }
                File.WriteAllText(AnalyzePath, builder.ToString());
            }
            catch (System.Exception e)
            {
                Context.LogError(e.ToString());
                return false;
            }
            return true;
        }
    }
    internal class BuildBundleInfo : PiplineBase
    {
        public override bool Execute()
        {
            var assetToBundleMap = new Dictionary<string, BundleAssetsData>();
            var bundleMap = new Dictionary<string, BundleAssetsData>();
            foreach (var item in Context.Assets)
            {
                if (!bundleMap.TryGetValue(item.BundleName, out var bundle))
                {
                    bundle = new BundleAssetsData
                    {
                        BundleName = item.BundleName
                    };
                    bundleMap.Add(item.BundleName, bundle);
                }
                if (item.BundleInfo != null && item.BundleInfo.AssetType == AssetType.Scene)
                {
                    //Cannot mark assets and scenes in one AssetBundle. AssetBundle name is "assets/res/scenes/map0.assetbundle".
                    bundle.IsScene = true;
                }
                bundle.Assets.Add(item);
                if (!assetToBundleMap.ContainsKey(item.AssetPath))
                {
                    assetToBundleMap.Add(item.AssetPath, bundle);
                }
            }
            foreach (var item in Context.Assets)
            {
                var path = item.AssetPath;
                if (!assetToBundleMap.TryGetValue(path, out var currentBundle))
                {
                    return false;
                }
                if (item.IsEditor)
                {
                    continue;
                }
                var dir = Path.GetDirectoryName(path);
                var fileName = Path.GetFileNameWithoutExtension(path);
                var bundleName = Path.Combine(dir, fileName).Replace("\\", "/");
                path = bundleName;
                int max = 50;
                bool find = false;
                while (!string.IsNullOrEmpty(path) && max-- > 0)
                {
                    if (bundleMap.TryGetValue(path, out var bundle))
                    {
                        // 资源加载方法为先找同名文件，找不到向路径上级查找，如果找到则认为在此文件中。
                        if (!bundle.Assets.Contains(item))
                        {
                            //当前bundle里移除自己
                            currentBundle.Assets.Remove(item);
                            bool moveAsset = true;
                            if (moveAsset)
                            {
                                //如果最近的bundle不包含当前资源，则添加
                                bundle.Assets.Add(item);
                            }
                            else
                            {
                                //或者给当前文件单独添加一个bundle
                                item.BundleName = bundleName;
                                bundle = new BundleAssetsData
                                {
                                    BundleName = item.BundleName
                                };
                                bundle.Assets.Add(item);
                                bundleMap.Add(item.BundleName, bundle);
                            }
                        }
                        find = true;
                        break;
                    }
                    path = Path.GetDirectoryName(path).Replace("\\", "/");
                }
                if (!find)
                {
                    Context.LogError(item.AssetPath + " 没有找到bundle " + item.BundleName);
                    return false;
                }
            }
            string pattern = @"[*:<>]+|[\u4e00-\u9fa5]";
            var regex = new Regex(pattern);
            Context.Bundles.Clear();
            foreach (var item in bundleMap)
            {
                var bundle = item.Value;
                if (regex.IsMatch(bundle.BundleName))
                {
                    Context.LogError("路径中存在非法字符" + bundle.BundleName);
                    bundle.BundleName = "hash-" + StringToHashCode(bundle.BundleName);
                }
                if (!bundle.BundleName.StartsWith("Assets/"))
                {
                    bundle.BundleName = "Assets/" + bundle.BundleName;
                }
                if (bundle.Assets.Count < 1)
                {
                    continue;
                }
                if (bundle.IsScene)
                {
                    var len = bundle.Assets.Count;
                    while (len-- > 0)
                    {
                        var asset = bundle.Assets[len];
                        if (asset.BundleInfo == null)
                        {
                            bundle.Assets.RemoveAt(len);
                            continue;
                        }
                        if (asset.BundleInfo.AssetType != AssetType.Scene)
                        {
                            bundle.Assets.RemoveAt(len);
                            continue;
                        }
                    }
                    //最终应该只有scene文件,不然会报下边的错
                    //Cannot mark assets and scenes in one AssetBundle. AssetBundle name is "assets/res/scenes/map0.assetbundle".
                }
                Context.Bundles.Add(bundle);
            }
            return true;
        }
        private int StringToHashCode(string input)
        {
            unchecked // 允许溢出，这通常用于哈希函数以提高分布性  
            {
                int hash = 17; // 初始哈希值（可以是任何非零整数）  
                foreach (char c in input)
                {
                    hash = hash * 23 + c; // 使用一个质数乘法器和加法器  
                }
                return hash;
            }
        }
    }

    internal class Build : PiplineBase
    {
        public override bool Execute()
        {
            List<AssetBundleBuild> buildList = new List<AssetBundleBuild>();
            var variantName = Context.Config.BundleExtension.Trim();
            if (variantName.StartsWith("."))
            {
                variantName = variantName.Substring(1);
            }
            var assets = new List<string>();
            foreach (var item in Context.Bundles)
            {
                var bundle = item;
                if (bundle.Assets.Count < 1)
                {
                    continue;
                }
                var bundleName = bundle.BundleName.ToLower();
                assets.Clear();
                foreach (var asset in bundle.Assets)
                {
                    assets.Add(asset.AssetPath);
                }
                var build = new AssetBundleBuild
                {
                    assetBundleVariant = variantName,
                    assetBundleName = bundleName,
                    assetNames = assets.ToArray()
                };
                buildList.Add(build);
            }

            if (buildList.Count < 1)
            {
                return true;
            }
            var outputPath = Context.Config.GetBuildCachePath();

            if (!Directory.Exists(outputPath))
            {
                Directory.CreateDirectory(outputPath);
            }
            Context.Manifest = BuildPipeline.BuildAssetBundles(outputPath, buildList.ToArray(), Context.Config.Options, Context.Config.BuildTarget);
            if (Context.Manifest == null)
            {
                return false;
            }
            return true;
        }
    }
    //--------------------------
    internal class BuildManifest : PiplineBase
    {
        public override bool Execute()
        {
            var manifest = Context.Manifest;
            var ext = Context.Config.GetBundleExtension();
            var targetPath = Context.Config.GetBuildCachePath();
            var blist = manifest.GetAllAssetBundles();
            if (blist.Length > 65535)
            {
                Context.LogErrorFormat("资源数量{0}过多，请检查是否合理", blist.Length);
                return false;
            }
            bool hasHash = Context.Config.Options.HasFlag(BuildAssetBundleOptions.AppendHashToAssetBundleName);
            var bundleMap = new Dictionary<string, MetaBundleInfo>();
            var nameList = new List<string>();
            foreach (var item in Context.Bundles)
            {
                var name = GetBundleName(item.BundleName);
                var info = new MetaBundleInfo();
                bundleMap.Add(name, info);
                nameList.Clear();
                foreach (var a in item.Assets)
                {
                    nameList.Add(a.AssetPath);
                }
                info.Assets = nameList.ToArray();
            }
            var list = new List<MetaBundleInfo>();
            foreach (var item in blist)
            {
                var name = GetBundleName(item, hasHash);
                if (!bundleMap.TryGetValue(name, out var bundle))
                {
                    Context.LogError("error " + item + " " + name);
                    continue;
                }
                bundle.Name = item;
                list.Add(bundle);
                var hash128 = manifest.GetAssetBundleHash(item);
                Utils.Unity.ConvertHash128ToInt(hash128, out bundle.Hash0, out bundle.Hash1);
                var filePath = Path.Combine(targetPath, item).Replace("\\", "/");
                if (!File.Exists(filePath))
                {
                    Context.LogErrorFormat("{0} 不存在", filePath);
                    return false;
                }

                var dlist = manifest.GetDirectDependencies(item);
                if (dlist.Length < 1) continue;
                if (dlist.Length > 127)
                {
                    Context.LogErrorFormat("{0}依赖项数 {1} 超出限制,请重新组织资源", item, dlist.Length);
                    return false;

                }
                else if (dlist.Length > 10)
                {
                    Context.LogErrorFormat("{0}依赖项数 {1} 异常,请检查是否合理", item, dlist.Length);
                }
                nameList.Clear();
                foreach (var d in dlist)
                {
                    nameList.Add(d);
                }
                bundle.DirectDependencies = nameList.ToArray();
            }

            var assetInfo = new MetaData
            {
                BuildTarget = Context.Config.BuildTarget.ToString(),
                BuildTime = DateTime.Now.ToString(),
                BundleExtension = ext,
                UnityVersion = Application.unityVersion,
                AssetCount = Context.Assets.Count,
                Bundles = list.ToArray(),
                AppendHashToAssetBundleName = hasHash
            };
            var versionPath = Path.Combine(targetPath, "Metadata.json");
            try
            {
                if (File.Exists(versionPath))
                {
                    File.Delete(versionPath);
                }
                File.WriteAllText(versionPath, Utils.Json.ToJson(assetInfo));
            }
            catch (System.Exception e)
            {
                Context.LogError(e.ToString());
                return false;
            }
            return true;
        }

    }
    //--------------------------
    internal class Export : PiplineBase
    {
        private MetaData MetaData;
        private Dictionary<string, MetaBundleInfo> Bundles = new Dictionary<string, MetaBundleInfo>();
        private List<MetaBundleInfo> encryptList = new List<MetaBundleInfo>();
        private List<MetaBundleInfo> BulitinList = new List<MetaBundleInfo>();
        private List<MetaBundleInfo> RawList = new List<MetaBundleInfo>();

        public override bool Execute()
        {
            var config = Context.Config;
            var targetPath = config.GetOutputPath();

            var cachePath = config.GetBuildCachePath();
            var metadata = Path.Combine(cachePath, "Metadata.json");
            if (!File.Exists(metadata))
            {
                Context.LogError("请先构建资源");
                return false;
            }
            try
            {
                if (Directory.Exists(targetPath))
                {
                    Directory.Delete(targetPath, true);
                }
            }
            catch (System.Exception)
            {
                throw;
            }
            MetaData = Utils.Json.ToObject<MetaData>(File.ReadAllText(metadata));
            var bundles = new List<MetaBundleInfo>(MetaData.Bundles);
            foreach (var bundle in bundles)
            {
                var src = Path.Combine(cachePath, bundle.Name);
                if (!File.Exists(src))
                {
                    Context.LogErrorFormat("{0} 不存在", src);
                    return false;
                }
            }
            foreach (var bundle in bundles)
            {
                var src = Path.Combine(cachePath, bundle.Name);
                var dst = Path.Combine(targetPath, bundle.Name);
                var dir = Path.GetDirectoryName(dst);
                if (!Directory.Exists(dir))
                {
                    Directory.CreateDirectory(dir);
                }
                File.Copy(src, dst);
                Bundles.Add(bundle.Name, bundle);
            }
            readSetting();
            export();
            Context.LogWarning(config.GetOutputRootPath());
            copy();
            return true;
        }
        private void getbundleList(string name, Dictionary<string, MetaBundleInfo> map)
        {
            if (map.ContainsKey(name))
            {
                return;
            }
            if (this.Bundles.TryGetValue(name, out var bundle))
            {
                map.Add(name, bundle);
                if (bundle.DirectDependencies == null)
                {
                    return;
                }
                foreach (var item in bundle.DirectDependencies)
                {
                    getbundleList(item, map);
                }
            }
        }
        private void readSetting()
        {
            var config = Context.Config;
            var targetPath = config.GetOutputPath();
            var settingMap = new Dictionary<string, BundleFileSetting>();
            var lines = config.GetEncryptFiles();
            //加密配制
            foreach (var item in lines)
            {
                var name = item.Replace("\\", "/");
                int matchMode = 1;
                if (File.Exists(item))
                {

                }
                else if (Directory.Exists(item))
                {
                    matchMode = 2;
                }
                else
                {
                    continue;
                }
                if (!settingMap.TryGetValue(name, out var setting))
                {
                    setting = new BundleFileSetting();
                    settingMap.Add(name, setting);
                }
                setting.Encrypt = Mathf.Max(matchMode, setting.Encrypt);
                setting.Path = name;
            }

            lines = config.GetBulitinFiles();
            //内置资源配制
            foreach (var item in lines)
            {
                var name = item.Replace("\\", "/");
                int matchMode = 1;
                if (File.Exists(item))
                {

                }
                else if (Directory.Exists(item))
                {
                    matchMode = 2;
                }
                else
                {
                    continue;
                }
                if (!settingMap.TryGetValue(name, out var setting))
                {
                    setting = new BundleFileSetting();
                    settingMap.Add(name, setting);
                }
                setting.BuiltIn = Mathf.Max(matchMode, setting.Encrypt);
                setting.Path = name;
            }

            if (settingMap.Count > 0)
            {
                var encryptMap = new Dictionary<string, bool>();
                var buitinMap = new Dictionary<string, bool>();
                foreach (var item in settingMap)
                {
                    var setting = item.Value;
                    foreach (var bundle in MetaData.Bundles)
                    {
                        foreach (var asset in bundle.Assets)
                        {
                            if (setting.Encrypt == 1 && asset == setting.Path)
                            {
                                if (!encryptMap.ContainsKey(bundle.Name))
                                {
                                    encryptMap.Add(bundle.Name, true);
                                }
                            }
                            else if (setting.Encrypt == 2 && asset.StartsWith(setting.Path))
                            {
                                if (!encryptMap.ContainsKey(bundle.Name))
                                {
                                    encryptMap.Add(bundle.Name, true);
                                }
                            }
                            if (setting.BuiltIn == 1 && asset == setting.Path)
                            {
                                if (!buitinMap.ContainsKey(bundle.Name))
                                {
                                    buitinMap.Add(bundle.Name, true);
                                }
                            }
                            else if (setting.BuiltIn == 2 && asset.StartsWith(setting.Path))
                            {
                                if (!buitinMap.ContainsKey(bundle.Name))
                                {
                                    buitinMap.Add(bundle.Name, true);
                                }
                            }
                        }
                    }
                }
                foreach (var item in encryptMap)
                {
                    if (!this.Bundles.TryGetValue(item.Key, out var bundle))
                    {
                        continue;
                    }
                    encryptList.Add(bundle);
                }
                var list = new Dictionary<string, MetaBundleInfo>();
                foreach (var item in buitinMap)
                {
                    if (!this.Bundles.ContainsKey(item.Key))
                    {
                        continue;
                    }
                    getbundleList(item.Key, list);
                }
                foreach (var item in list)
                {
                    if (!this.Bundles.TryGetValue(item.Key, out var bundle))
                    {
                        continue;
                    }
                    this.BulitinList.Add(bundle);
                }
            }



        }

        private void export()
        {
            var config = Context.Config;
            var cachePath = config.GetBuildCachePath();
            var targetPath = config.GetOutputPath();
            var len = MetaData.Bundles.Length;
            var encryptMap = new Dictionary<string, bool>();
            foreach (var item in this.encryptList)
            {
                if (this.Bundles.TryGetValue(item.Name, out var bundle))
                {
                    var src = Path.Combine(targetPath, item.Name);
                    if (File.Exists(src))
                    {
                        if (Context.Processor.Encrypt(src))
                        {
                            encryptMap.Add(item.Name, true);
                        }
                    }
                }
            }
            foreach (var item in this.BulitinList)
            {
                //Context.LogError("bulitin " + item.Name);
            }

            var list = new List<BundleInfo>();
            for (int i = 0; i < len; i++)
            {
                var d = MetaData.Bundles[i];
                var binfo = new BundleInfo();
                list.Add(binfo);
                binfo.Path = d.Name;
                binfo.Hash0 = d.Hash0;
                binfo.Hash1 = d.Hash1;
                binfo.Encrypt = 0;
                if (encryptMap.TryGetValue(d.Name, out var encrypt))
                {
                    binfo.Encrypt = 1;
                    binfo.Hash0 = d.Hash0 + 1;
                    binfo.Hash1 = d.Hash1 + 1;
                }

                var filePath = Path.Combine(targetPath, d.Name);
                var finfo = new FileInfo(filePath);
                binfo.Size = (int)finfo.Length;
                if (binfo.Size < 32)
                {
                    Context.LogErrorFormat("{0} 出错了,没有生成正确的资源!", d.Name);
                    Context.LogError("请在日志中搜索关键字<  Building AssetBundle failed  >");
                }

            }
            list.Sort(SortInfo);
            var ext = config.GetBundleExtension();
            //名字转索引
            var name2Index = new Dictionary<string, int>();
            for (int i = 0; i < list.Count; i++)
            {
                var name = list[i].Path;
                name2Index.Add(name, i);
            }

            //依赖存索引
            for (int i = 0; i < list.Count; i++)
            {
                var name = list[i].Path;
                if (!this.Bundles.TryGetValue(name, out var binfo))
                {
                    Context.LogErrorFormat("{0} 没有找到配制!", name);
                }
                if (binfo.DirectDependencies != null && binfo.DirectDependencies.Length > 0)
                {
                    var idlist = new List<ushort>();
                    foreach (var item in binfo.DirectDependencies)
                    {
                        idlist.Add((ushort)name2Index[item]);
                    }
                    list[i].DirectDependencies = idlist.ToArray();
                }
                else
                {
                    list[i].DirectDependencies = new ushort[0];
                }

                var dir = Path.GetDirectoryName(name);
                var f = Path.GetFileNameWithoutExtension(name);
                list[i].Path = Path.Combine(dir, f).Replace("\\", "/");
            }

            var lines = config.GetRawFiles();
            //原始资源
            foreach (var item in lines)
            {
                if (System.IO.Directory.Exists(item))
                {
                    var dname = item.Replace("\\", "/");
                    Debug.LogError(dname);
                    var files = System.IO.Directory.GetFiles(item, "*.*", SearchOption.AllDirectories);
                    foreach (var f in files)
                    {
                        if (Path.GetExtension(f) == ".meta")
                        {
                            continue;
                        }
                        var binfo = new BundleInfo();
                        list.Add(binfo);

                        binfo.Encrypt = 0;
                        binfo.DirectDependencies = new ushort[0];
                        var finfo = new FileInfo(f);
                        binfo.Size = (int)finfo.Length;
                        using (var md5 = MD5.Create())
                        using (var stream = File.OpenRead(f))
                        {
                            byte[] hashBytes = md5.ComputeHash(stream);
                            var h128 = Hash128.Parse(BitConverter.ToString(hashBytes));
                            Utils.Unity.ConvertHash128ToInt(h128, out binfo.Hash0, out binfo.Hash1);
                        }
                        binfo.Path = f.Replace("\\", "/").Replace(dname, "");
                        if (binfo.Path.StartsWith("/"))
                        {
                            binfo.Path = binfo.Path.Substring(1);
                        }
                        binfo.Path = "raw/" + binfo.Path;
                    }
                }
            }


            StringBuilder versionBuilder = new StringBuilder();

            versionBuilder.Clear();
            versionBuilder.AppendLine(len + "");
            for (int i = 0; i < len; i++)
            {
                var item = list[i];
                versionBuilder.AppendLine(item.Path);
                versionBuilder.Append(item.Hash0);
                versionBuilder.Append(item.Hash1);
                versionBuilder.AppendLine();
                versionBuilder.AppendLine(item.Size + "");
                versionBuilder.AppendLine(item.Encrypt + "");
            }
            var versionData = System.Text.Encoding.UTF8.GetBytes(versionBuilder.ToString());

            var info = new AssetsInfo();
            info.Version = Utils.File.GetMD5(versionData);
            info.BundleExtension = MetaData.BundleExtension;
            info.BuildTime = DateTime.Now.ToString();
            info.UnityVersion = MetaData.UnityVersion;
            info.BuildTarget = MetaData.BuildTarget;
            info.AssetCount = MetaData.AssetCount;
            info.UserData = config.UserData;
            info.AppendHashToAssetBundleName = MetaData.AppendHashToAssetBundleName;
            info.Bundles = list.ToArray();

            var versionPath = Path.Combine(targetPath, config.GetManifestFileName() + ".json");
            try
            {
                if (File.Exists(versionPath))
                {
                    File.Delete(versionPath);
                }
                File.WriteAllText(versionPath, Utils.Json.ToJson(info));
            }
            catch (System.Exception e)
            {
                Debug.LogException(e);
            }

            versionPath = Path.Combine(targetPath, config.GetManifestFileName());
            try
            {
                if (File.Exists(versionPath))
                {
                    File.Delete(versionPath);
                }
                File.WriteAllBytes(versionPath, AssetsInfo.EncodeToBytes(info));
            }
            catch (System.Exception e)
            {
                Debug.LogException(e);
            }

        }

        private int SortInfo(BundleInfo a, BundleInfo b)
        {
            return b.Size.CompareTo(a.Size);
        }
        private void copy()
        {
            var config = Context.Config;
            var targetPath = config.GetOutputPath();
            if (!File.Exists(Path.Combine(config.GetOutputRootPath() + ".txt")))
            {
                File.WriteAllText(Path.Combine(config.GetOutputRootPath() + ".txt"), "#自定义导出目录如:\n" + Application.streamingAssetsPath);
            }
            if (File.Exists(Path.Combine(config.GetOutputRootPath() + ".txt")))
            {
                Debug.LogWarning("read copy config file " + Path.Combine(config.GetOutputRootPath() + ".txt"));
                var s = File.ReadAllLines(Path.Combine(config.GetOutputRootPath() + ".txt"));
                foreach (var line in s)
                {
                    var dst = line.Trim();
                    if (dst.StartsWith("#"))
                    {
                        continue;
                    }
                    dst = Path.Combine(dst, config.CustomFolder);
                    if (Directory.Exists(dst))
                    {
                        Directory.Delete(dst, true);
                    }
                    CopyFolder(targetPath, dst);
                    Debug.LogWarning("copy " + targetPath + " to " + dst);
                }
            }
        }
        private int CopyFolder(string sourceFolder, string destFolder)
        {
            try
            {
                //如果目标路径不存在,则创建目标路径
                if (!System.IO.Directory.Exists(destFolder))
                {
                    System.IO.Directory.CreateDirectory(destFolder);
                }
                //得到原文件根目录下的所有文件
                string[] files = System.IO.Directory.GetFiles(sourceFolder);
                foreach (string file in files)
                {
                    string name = System.IO.Path.GetFileName(file);
                    string dest = System.IO.Path.Combine(destFolder, name);
                    System.IO.File.Copy(file, dest);//复制文件
                }
                //得到原文件根目录下的所有文件夹
                string[] folders = System.IO.Directory.GetDirectories(sourceFolder);
                foreach (string folder in folders)
                {
                    string name = System.IO.Path.GetFileName(folder);
                    string dest = System.IO.Path.Combine(destFolder, name);
                    CopyFolder(folder, dest);//构建目标路径,递归复制文件
                }
                return 1;
            }
            catch (Exception)
            {
                return -1;
            }
        }

    }
}